namespace CCStatistics.Domain.Api
{
    using System;
    using System.Collections.Generic;
    using System.IO;

    using Interfaces;

    using ThoughtWorks.CruiseControl.Core;

    /// <summary>
    /// Contains the logic for recalculating statistics for a CC.Net project
    /// </summary>
    public class ProjectStatisticsPresenter
    {
        // Model/View/Presenter -- View

        private const string StatisticsCsvFileName = "statistics.csv";
        //private const string ReportsImageFileName = "report.bmp";
        private const string ReportsFileName = "report.xml";

        private readonly ICCStatsProject _project;

        private readonly ICCStatsView _view;

        /// <summary>
        /// Initializes a new instance of the <see cref="ProjectStatisticsPresenter"/> class.
        /// </summary>
        /// <param name="iccStatsView">The icc stats view.</param>
        /// <param name="project">The project.</param>
        /// <param name="statsPublisher">The stats publisher.</param>
        /// <param name="buildLogDirectory">The build log directory.</param>
        public ProjectStatisticsPresenter(ICCStatsView iccStatsView,
                                          IProject project,
                                          ICCStatisticsPublisher statsPublisher,
                                          String buildLogDirectory)
        {
            _view = iccStatsView;
            _project = new CCStatsProject(project, statsPublisher, buildLogDirectory);
        }

        /// <summary>
        /// Recalculates the statistics for a particular project replacing the previous generated statistics files.
        /// </summary>
        public void RecalculateStatistics()
        {
            _view.UpdateViewFromActiveStatistics(_project.StatisticsForPublisher, _project.Name);
            var startTime = OutputStartupToView();
            var cancelAbort = false;

            ArchiveExistingStatistics();

            var logFileNames = GetSortedHistoricalLogFileNames();

            _view.InitialiseProgress(logFileNames.Count);

            var nextLabel = 1;

            logFileNames.ForEach(logFileName =>
            {
                _view.WriteDetailLine("Processing... " + logFileName);
                nextLabel = ProcessLogFile(_project.StatsPostPublisher, logFileName, nextLabel);
                _view.IncrementProgress();
            });

            FinishExecution(startTime, cancelAbort);
        }

        /// <summary>
        /// Processes the log file.
        /// </summary>
        /// <param name="StatisticsPublisherWrapper">The cc statistics publisher override.</param>
        /// <param name="logFileName">Name of the log file.</param>
        /// <param name="nextLabel">The last label obtained, usually from a previous successful build of the project</param>
        public int ProcessLogFile(ICCStatisticsPublisher StatisticsPublisherWrapper, String logFileName, int nextLabel)
        {
            if (StatisticsPublisherWrapper == null)
                throw new ArgumentNullException("StatisticsPublisherWrapper");

            return StatisticsPublisherWrapper.ProcessLogFile(logFileName, _project, nextLabel);
        }

        private List<String> GetSortedHistoricalLogFileNames()
        {
            var resultBuilds = new List<string>(_project.IntegrationRepository.GetBuildNames());

            // This part is critical
            resultBuilds.Sort();
            return resultBuilds;
        }

        private void FinishExecution(DateTime startTime, bool cancelAbort)
        {
            var endTime = DateTime.Now;
            var elapsedTime = endTime.Subtract(startTime);
            _view.WriteLine("*** Ended at: " + startTime + " - Time taken: " + elapsedTime);

            _view.EndExecutionRun(_project.Name, cancelAbort);
        }

        private DateTime OutputStartupToView()
        {
            return _view.RenderProjectStart(_project.Name, DateTime.Now);
        }

        /// <summary>
        /// Archive off the previous statistics.xml file, report image and csv file.
        /// </summary>
        private void ArchiveExistingStatistics()
        {
            //because we now update the existing file, we just backup a copy of it
            ArchiveFile(Path.Combine(_project.ArtifactDirectory, ReportsFileName), true);
            
            //we don't (any longer?) recreate the image files, so should not archive any existing file...
            //ArchiveFile(Path.Combine(_project.ArtifactDirectory, ReportsImageFileName), false);

            //TODO - until the statistics.csv update is re-implemented and enabled, we should not Archive the existing...
            //ArchiveFile(Path.Combine(_project.ArtifactDirectory, StatisticsCsvFileName), false);
        }

        /// <summary>
        /// Archive/backup the specified file if it exists by adding ".old".
        /// </summary>
        private void ArchiveFile(string fileName, bool leaveOriginalInPlace)
        {
            if (!File.Exists(fileName))
                return;

            var archivedFileName = fileName + ".old";
            if (File.Exists(archivedFileName))
                File.Delete(archivedFileName);

            _view.WriteDetailLine("Archiving a backup copy of file " + fileName);

            if (leaveOriginalInPlace)
                File.Copy(fileName, archivedFileName);
            else
                File.Move(fileName, archivedFileName);
        }

        /// <summary>
        /// Gets the CC config path.
        /// </summary>
        /// <param name="applicationPath">The application path.</param>
        /// <returns></returns>
        public static string GetCCConfigPath(String applicationPath)
        {
            const String ccnetConfigFile = "ccnet.config";
            const String ccnetServerDir = @"CruiseControl.Net\server";

            string startupCCConfigPath;
            var programFilesPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);

            if (!String.IsNullOrEmpty(programFilesPath))
            {
                startupCCConfigPath = Path.Combine(programFilesPath, ccnetServerDir);
                if (Directory.Exists(startupCCConfigPath))
                {
                    startupCCConfigPath = Path.Combine(startupCCConfigPath, ccnetConfigFile);

                    if (!File.Exists(startupCCConfigPath))
                        startupCCConfigPath = programFilesPath;
                }
            }
            else
                startupCCConfigPath = applicationPath;

            return startupCCConfigPath;
        }

        /// <summary>
        /// Loads the CC net config file.
        /// </summary>
        /// <param name="configFileInfo">The config file info.</param>
        public static IConfiguration LoadCCNetConfigFile(FileInfo configFileInfo)
        {
            var fileLoader = new CCDefaultConfigurationFileLoader();
            return fileLoader.Load(configFileInfo);
        }
    }
}